import { get, post } from '../../http'
import { typeOf } from '@/utils/cloud/utils'
import { format } from 'date-fns'
import { InfluxDB, Point, flux } from './influxdbdata.mjs'
import lodash from 'lodash'

const tableWhiteList = ['all_power', 'all_energy', 'all_state', 'all_alert', 'all_economic']

export const getInfluxdb = async (params: any, name: string): Promise<any> => {
  let {
    component_ids,
    inner_aggregate,
    outer_aggregate,
    subtotal_name,
    filter_conditions,
    startDateTime,
    endDateTime,
    channel,
    fillType,
    time = '1m',
    limit = 50,
    table = 'COMPONENT_DATA'
  } = params

  if (!tableWhiteList.includes(table)) {
    table = 'COMPONENT_DATA'
  }

  const funcHash = {
    sum: 'SUM',
    avg: 'MEAN',
    last: 'LAST'
  }

  function transformString(str: any, needUpper: boolean = false) {
    // 使用正则表达式捕获三种情况：last, sum, avg 后面跟反引号内的内容
    // 并确保替换时保留原始的反引号内容
    str = str.replace(/(last|sum|avg|first|delta|max|min)\(`([^`]+)`\)/g, (match: any, func: any, identifier: any) => {
      // 如果捕获到的是 avg，则替换为 MEAN
      const newFunc = funcHash[func as keyof typeof funcHash] || func
      // 转换标识符为大写
      const newIdentifier = needUpper
        ? identifier.replace(/_([a-z])/g, g => g[1].toUpperCase()).replace(/^./, (first: any) => first.toUpperCase())
        : identifier
      // 构造新的字符串，注意这里我们不再需要 `\s*`\2``，因为我们只替换括号内的内容
      return `${newFunc}(\`${newIdentifier}\`)`
    })
    let trimmedStr = str.trimRight()
    const lastSpaceIndex = trimmedStr.lastIndexOf(' ')
    const newString = str.slice(0, lastSpaceIndex + 1) + 'AS ' + str.slice(lastSpaceIndex + 1)
    return newString.replace(/`/g, '')
  }
  let inner = ''
  if (inner_aggregate) {
    let innerArr = inner_aggregate.split(',').map((item: string) => {
      if (item.includes('delta(`')) {
        const arr = item.split(' ')
        let first = arr[0].replace('delta(`', 'first(`')
        let last = arr[0].replace('delta(`', 'last(`')
        delete arr[0]
        item = `${last} - ${first} ${arr.join(' ')}`
      }
      return transformString(item, true)
    })

    inner = innerArr.join(',')
  }
  let outer = ''
  if (outer_aggregate) {
    let outerArr = outer_aggregate.split(',').map((item: string) => {
      return transformString(item, false)
    })

    outer = outerArr.join(',')
  }
  // component_ids = ['h9wbEtY42Li#I055JFVRQbufgVTO4jH_qg@iot', 'h9wbEtY42Li#Cr_ZUzLXTwaFZl-yQ8QRfQ@iot']
  let componentIdFilter = ''
  if (component_ids) {
    componentIdFilter = `(${component_ids.map((item: string) => `componentId::tag='${item}'`).join(' OR ')})`
  }
  let sql = ''
  switch (name) {
    case 'energy_subsystem_latest':
      sql = `SELECT ${outer} FROM (SELECT ${inner}, time FROM ${table} WHERE ${componentIdFilter} GROUP BY componentId) WHERE time>='${startDateTime}' ${
        endDateTime ? "AND time<='" + endDateTime + "'" : ''
      } ${filter_conditions && filter_conditions !== true ? 'AND ' + filter_conditions : ''} tz('${
        import.meta.env.VITE_INFLUXDB_TIMEZONE
      }')`
      break
    case 'general_cumulative_query':
      sql = `SELECT ${outer} FROM (SELECT ${inner}, time FROM ${table} WHERE ${componentIdFilter} ${
        startDateTime ? "AND time>='" + startDateTime + "'" : ''
      } ${endDateTime ? "AND time<='" + endDateTime + "'" : ''}) ${
        filter_conditions && filter_conditions !== true ? 'WHERE ' + filter_conditions : ''
      } tz('${import.meta.env.VITE_INFLUXDB_TIMEZONE}')`
      break
    case 'general_instant_query':
      sql = `SELECT ${inner}, time,iotId FROM ${table} WHERE ${componentIdFilter} ${
        startDateTime ? "AND time>='" + startDateTime + "'" : ''
      } ${endDateTime ? "AND time<='" + endDateTime + "'" : ''} GROUP BY componentId tz('${
        import.meta.env.VITE_INFLUXDB_TIMEZONE
      }')`
      break
    case 'energy_subsystem_statistic_by_time_window':
      time = time.replace('dc', 'd')
      // select time, '####subtotal_name####' as subtotal_name, ####outer_aggregate#### from (select /*+ PREDOWNSAMPLE */ time, iot_id, ####inner_aggregate#### from ####table#### where domain_id='####domain_id####' and iot_id in ('####iot_id####') and time >= '####startDateTime####' and time <= '####endDateTime####' and (ifnull(####filter_conditions####, '')='' or ####filter_conditions####) sample by ####time#### fill ####fillType####) group by time order by time
      sql = `SELECT ${outer} FROM (SELECT ${inner}, time FROM ${table} WHERE ${componentIdFilter} GROUP BY time(${time}) ) WHERE time>='${startDateTime}' ${
        endDateTime ? "AND time<='" + endDateTime + "'" : ''
      } ${
        filter_conditions && filter_conditions !== true ? 'AND ' + filter_conditions : ''
      } GROUP BY time(${time}) tz('${import.meta.env.VITE_INFLUXDB_TIMEZONE}')`
      break
    case 'channel_time_data':
      let channelName = channel
        .replace(/_([a-z])/g, (g: any) => g[1].toUpperCase())
        .replace(/^./, (first: any) => first.toUpperCase())
      sql = `SELECT MEAN(${channelName}) AS ${lodash.snakeCase(
        channelName
      )}, time FROM ${table} WHERE ${componentIdFilter} ${startDateTime ? "AND time>='" + startDateTime + "'" : ''} ${
        endDateTime ? "AND time<='" + endDateTime + "'" : ''
      } GROUP BY time(${time}) tz('${import.meta.env.VITE_INFLUXDB_TIMEZONE}')`
      break
    case 'device_event_records':
      // console.log(params)
      sql = `SELECT * FROM all_state WHERE time>='${startDateTime}' AND time<='${endDateTime}' AND ${componentIdFilter} AND State>1 LIMIT 1000 tz('${
        import.meta.env.VITE_INFLUXDB_TIMEZONE
      }')`
      break
    case 'operation_log':
      sql = `SELECT * FROM OPERATION_LOG WHERE time>='${startDateTime}' AND time<='${endDateTime}' LIMIT ${limit} tz('${
        import.meta.env.VITE_INFLUXDB_TIMEZONE
      }')`
      break
  }
  const res: any = await get('/query', {
    db: import.meta.env.VITE_INFLUXDB_BUCKET,
    q: sql
  })
  // subtotal_name
  if (!res.results[0].series) return []

  const getColData = (item: any, val: any) => {
    let obj: any = {}
    item.columns.forEach((col: string, index: number) => {
      if (col === 'iotId') col = 'iot_id'
      if (col === 'time') {
        obj[col] = format(new Date(val[index]), 'yyyy-MM-dd HH:mm:ss')
      } else {
        obj[col] = val[index]
      }
    })
    obj.subtotal_name = subtotal_name
    return obj
  }

  const result = res.results[0].series.map((item: any) => {
    if (typeOf(item.values[0]) === 'array') {
      return item.values.map((val: any) => {
        return getColData(item, val)
      })
    } else {
      return getColData(item, item.values[0])
    }
  })
  return result
}

export const getInfluxdb2 = async (params: any, name: string): Promise<any> => {
  let { startDateTime, endDateTime, component_ids, inner_aggregate, table = 'COMPONENT_DATA' } = params

  if (!tableWhiteList.includes(table)) {
    table = 'COMPONENT_DATA'
  }

  let fieldStr: string = ''
  if (inner_aggregate) {
    let channels = inner_aggregate.match(/`([^`]*)`/g)
    channels = channels.map((item: string) => item.slice(1, -1))
    channels = [...new Set(channels)]
    fieldStr = channels
      .map((item: string) => `r["_field"] == "${lodash.upperFirst(lodash.camelCase(item))}"`)
      .join(' or ')
  }

  let componentIdStr = ''
  if (component_ids) {
    componentIdStr = component_ids.map((item: string) => `r["componentId"] == "${item}"`).join(' or ')
  }

  const influxDB2 = new InfluxDB({
    url: '',
    token: import.meta.env.VITE_INFLUXDB_TOKEN
  })
  const org = 'linku'
  let sql: string = ''
  switch (name) {
    case 'general_instant_query':
      sql = `from(bucket: "edge")
      |> range(start: ${new Date(startDateTime).toISOString()}, stop: ${new Date(endDateTime).toISOString()})
      |> filter(fn: (r) => r["_measurement"] == "${table}")
      |> filter(fn: (r) => ${fieldStr})
      |> filter(fn: (r) => ${componentIdStr})
      |> last()
      |> yield()`
      break
    case 'device_event_records':
      sql = `from(bucket: "edge")
      |> range(start: ${new Date(startDateTime).toISOString()}, stop: ${new Date(endDateTime).toISOString()})
      |> filter(fn: (r) => r["_measurement"] == "${table}")
      |> filter(fn: (r) => r["_field"] == "State" and r["_value"] > 1)
      |> last()
      |> yield()`
      break
  }

  const queryApi = influxDB2.getQueryApi(org)
  let list: any = []
  return await new Promise((resolve, reject) => {
    queryApi.queryRows(sql, {
      next(row: any, tableMeta: any) {
        const o = tableMeta.toObject(row)
        list.push(o)
      },
      error(error: any) {
        console.log('QUERY FAILED', error)
      },
      complete() {
        let result: any = {
          time: [],
          iot_id: []
        }
        list.forEach((item: any) => {
          let key = lodash.snakeCase(item._field)
          result[key] = []
        })
        list.forEach((item: any) => {
          let channel = lodash.snakeCase(item._field)

          for (let key in result) {
            if (key === 'time') {
              result[key].unshift(format(new Date(item._time), 'yyyy-MM-dd HH:mm:ss'))
            } else if (key === 'iot_id') {
              result[key].unshift(item.iotId)
            } else if (key === channel) {
              result[key].unshift(item._value)
            } else {
              result[key].unshift(null)
            }
          }
        })
        resolve([result])
      }
    })
  })
}

export const writeChannel = async (params: any) => {
  const { table = 'OPERATION_LOG', componentId, alias, domainId, iotId, productName, category, type, ...args } = params
  const data = `${table},componentId=${componentId},alias=${alias},domainId=${domainId},iotId=${iotId},productName=${productName},category=${category},type=${type} content="${JSON.stringify(
    args
  )}"`

  post(`/api/v2/write?org=linku&bucket=${import.meta.env.VITE_INFLUXDB_BUCKET}&precision=ns`, data as any, '', {
    Authorization: 'Token ' + import.meta.env.VITE_INFLUXDB_TOKEN,
    'Content-Type': 'text/plain; charset=utf-8'
  })
}
